﻿namespace OpenComposite.Repository.TFS
{
	using System;
	using System.Collections.Generic;
	using System.ComponentModel;
	using System.Diagnostics;
	using System.Drawing;
	using System.IO;
	using System.Linq;
	using System.Windows.Forms;

	using Aga.Controls.Tree;

	using Microsoft.TeamFoundation;
	using Microsoft.TeamFoundation.Client;
	using Microsoft.TeamFoundation.VersionControl.Client;

	using OpenComposite.EII;
	using OpenComposite.EII.Forms;

	using OpenComposite.Base.Collections;
	using OpenComposite.Repository.Properties;
	using OpenComposite.Repository.TFS;

	public partial class UCTfsExplorer : UserControl, ITfsExplorer
	{
		#region Initialization

		public UCTfsExplorer(OpenFileDialogEx opendlgex)
		{
			_opendlgex = opendlgex;

			InitializeComponent();

			dgvFiles.AutoGenerateColumns = false;
		}

		#endregion Initialization

		#region Public Members

		#region Properties

		public List<string> AppliedFilter
		{
			get
			{
				if ( _opendlgex.OpenFolder ) return new List<string>();
				string selectedFilter = cmbAllowedExtensions.SelectedValue.ToString();
				List<string> value = new List<string>();
				if ( !string.IsNullOrEmpty(selectedFilter) ) {
					string[] filters = selectedFilter.Split(';');
					foreach ( string filter in filters ) {
						value.Add(filter.Replace("*", ""));
					}
				}
				return value;
			}
		}

		public string FileName
		{
			get
			{
				if ( dgvFiles.SelectedRows.Count < 1 ) return null;
				TfItemNode node = dgvFiles.SelectedRows[0].DataBoundItem as TfItemNode;
				return node.Item.ServerItem;
			}
			set { _fileNameToSelect = value; }
		}

		public TeamFoundationServer TfServer
		{
			get { return _tfServer; }
			set
			{
				if ( value == null ) return;
				txtServerAddress.Text = value.Uri.AbsoluteUri;
			}
		}

		public string TfsUri
		{
			get
			{
				TeamFoundationServer tfs = this.TfServer;
				if ( tfs != null && tfs.Uri != null ) {
					return tfs.Uri.AbsoluteUri;
				} else {
					return null;
				}
			}
			set { this.TfServer = TeamFoundationWrapper.GetTeamFoundationServer(value); }
		}

		public Workspace Workspace
		{
			get { return cmbWorkspaces.SelectedItem as Workspace; }
			set { _workspaceToSelect = value; }
		}

		public string WorkspaceName
		{
			get
			{
				if ( this.Workspace != null ) {
					return this.Workspace.Name;
				} else {
					return null;
				}
			}
			set
			{
				if ( this.TfServer != null ) {
					this.Workspace = TeamFoundationWrapper.GetWorkspace(this.TfServer, value);
				}
			}
		}

		#endregion Properties

		#region Methods

		public bool Init()
		{
			bool ok = false;
			#region Init TFS Support (only if needed)
			if ( _opendlgex.OpenFolder ) {
				cmbAllowedExtensions.Visible = false;
				label1.Visible = false;
			} else {
				if ( string.IsNullOrEmpty(_opendlgex.Filter) ) _opendlgex.Filter = "All Files (*.*)|*.*";
				string[] filters = _opendlgex.Filter.Split('|');
				BindingListEx<ExtensionFilter> list = new BindingListEx<ExtensionFilter>();
				for ( int i = 0; i < filters.Length; i += 2 ) {
					list.Add(new ExtensionFilter(filters[i], filters[i + 1]));
				}
				cmbAllowedExtensions.DataSource = list;
			}
			#endregion
			#region Init Preset data (selected items)
			if ( !string.IsNullOrEmpty(txtServerAddress.Text) ) {
				lookupServer();
				foreach ( Workspace ws in cmbWorkspaces.Items ) {
					if ( ( _workspaceToSelect == null && ws.Computer == Environment.MachineName )
							||
						 ( _workspaceToSelect != null && ws.Name == _workspaceToSelect.Name ) ) {
						cmbWorkspaces.SelectedItem = ws;
						break;
					}
				}
				ok = true;
			}
			_workspaceToSelect = null;
			#endregion

			return ok;
		}

		#endregion Methods

		#endregion Public Members

		#region Private Members

		#region Fields

		private string _fileNameToSelect = null;
		private TreeModel _model = null;
		private OpenFileDialogEx _opendlgex;
		private Node _rootNode = null;
		private BindingListEx<TeamProject> _teamProjects = new BindingListEx<TeamProject>();
		private TeamFoundationServer _tfServer = null;
		private VersionControlServer _versionControl = null;
		private BindingListEx<Workspace> _workSpaces = new BindingListEx<Workspace>();
		private Workspace _workspaceToSelect = null;

		#endregion Fields

		#region Event Handlers

		private void btnLookup_Click(object sender, EventArgs e)
		{
			lookupServer();
		}

		private void dgvFiles_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
		{
			if ( e.RowIndex < 0 ) return;
			TfItemNode node = dgvFiles.Rows[e.RowIndex].DataBoundItem as TfItemNode;
			if ( node == null || node.Item == null || node.Item.ItemType != ItemType.Folder ) return;
			selectItem(node.Item.ServerItem);
		}

		private void dgvFiles_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
		{
			e.PaintBackground(e.ClipBounds, e.State == DataGridViewElementStates.Selected);
			if ( e.RowIndex < 0 ) return;
			if ( e.ColumnIndex == colImage.Index ) {
				TfItemNode item = dgvFiles.Rows[e.RowIndex].DataBoundItem as TfItemNode;
				ItemType type = (ItemType)e.Value;
				Bitmap icon = null;
				if ( type == ItemType.Folder ) {
					icon = Resources.folder_closed;
				} else if ( type == ItemType.File ) {
					icon = Helpers.GetFileIcon(item.LocalPath).ToBitmap();
					//icon = Icon.ExtractAssociatedIcon(item.LocalPath).ToBitmap();
					//icon = Resources.Document;
				}
				icon.MakeTransparent();
				Brush backBrush = e.State == DataGridViewElementStates.Selected ? new SolidBrush(e.CellStyle.SelectionBackColor) : new SolidBrush(e.CellStyle.BackColor);
				e.Graphics.FillRectangle(backBrush, e.CellBounds);
				Point imageLocation = new Point(e.CellBounds.X,
					e.CellBounds.Y + e.CellBounds.Height / 2 - icon.Height / 2);
				e.Graphics.DrawImage(icon, imageLocation);
				//e.Paint(e.ClipBounds, e.PaintParts);
				e.Handled = true;
			}
		}

		private void tvAdvTfs_Expanded(object sender, TreeViewAdvEventArgs e)
		{
		}

		private void tvAdvTfs_Expanding(object sender, TreeViewAdvEventArgs e)
		{
			Node node = e.Node.Tag as Node;
			if ( node == null ) return;
			if ( node.Nodes.Count != 0 ) {
				foreach ( Node child in node.Nodes ) {
					TfItemNode itemNode = child as TfItemNode;
					if ( itemNode == null ) continue;
					loadSubTree(itemNode);
				}
			} else {

			}
		}

		private void tvAdvTfs_NodeMouseClick(object sender, TreeNodeAdvMouseEventArgs e)
		{
		}

		private void tvAdvTfs_SelectionChanged(object sender, EventArgs e)
		{
			if ( tvAdvTfs.SelectedNode == null ) return;
			Node selectedNode = tvAdvTfs.SelectedNode.Tag as Node;
			if ( selectedNode == null ) return;
			TfTeamProjectNode projNode = selectedNode as TfTeamProjectNode;
			if ( projNode != null ) {
				BindingListEx<TfItemNode> items = getItems(projNode);
				dgvFiles.DataSource = items;
			} else {
				TfItemNode itemNode = selectedNode as TfItemNode;
				if ( itemNode != null ) {
					BindingListEx<TfItemNode> items = getItems(itemNode);
					dgvFiles.DataSource = items;
					lblLocalPath.Text = itemNode.LocalPath;
				}
			}
			dgvFiles.Sort(colImage, ListSortDirection.Ascending);
		}

		private void txtServerAddress_KeyDown(object sender, KeyEventArgs e)
		{
			if ( e.KeyCode == Keys.Enter ) {
				lookupServer();
				e.Handled = true;
			}
		}

		private void txtServerAddress_KeyUp(object sender, KeyEventArgs e)
		{
			if ( e.KeyCode == Keys.Enter ) {
				lookupServer();
			}
		}

		#endregion Event Handlers

		#region Methods

		private BindingListEx<TfItemNode> getItems(TfTeamProjectNode projNode)
		{
			BindingListEx<TfItemNode> items = new BindingListEx<TfItemNode>();
			if ( projNode == null ) return items;
			ItemSet itemSet = projNode.Project.VersionControlServer.GetItems(string.Format("$/{0}/*", projNode.Name));
			foreach ( Item item in itemSet.Items ) {
				//if ( item.ItemType != ItemType.File ) continue;
				if ( _opendlgex.OpenFolder && item.ItemType == ItemType.File ) continue;
				string ext = Path.GetExtension(item.ServerItem);
				if ( item.ItemType == ItemType.File &&
					 !AppliedFilter.Contains(ext) ) continue;
				TfItemNode child = new TfItemNode(item);
				items.Add(child);
			}
			return items;
		}

		private BindingListEx<TfItemNode> getItems(TfItemNode itemNode)
		{
			BindingListEx<TfItemNode> items = new BindingListEx<TfItemNode>();
			if ( itemNode == null ) return items;
			ItemSet itemSet = itemNode.Item.VersionControlServer.GetItems(string.Format("{0}/*", itemNode.Item.ServerItem));
			foreach ( Item item in itemSet.Items ) {
				//if ( item.ItemType != ItemType.File ) continue;
				if ( _opendlgex.OpenFolder && item.ItemType == ItemType.File ) continue;
				string ext = Path.GetExtension(item.ServerItem);
				if ( item.ItemType == ItemType.File &&
					 !AppliedFilter.Contains(ext) ) continue;
				TfItemNode child = new TfItemNode(item);
				items.Add(child);
			}
			return items;
		}

		private void loadPreSelectedItem()
		{
			selectItem(_fileNameToSelect);
			if ( string.IsNullOrEmpty(_fileNameToSelect) ) return;
			string[] steps = _fileNameToSelect.Split('/');
			TreeNodeAdv currentNode = tvAdvTfs.FindNodeByTag(_rootNode);
			currentNode.Expand();
			foreach ( string step in steps ) {
				if ( step == "$" ) continue; // root/server
				var items = from tn in currentNode.Children
							where ( tn.Tag is TfItemNode && ( tn.Tag as TfItemNode ).Name == step ) ||
								  ( tn.Tag is TfTeamProjectNode && ( tn.Tag as TfTeamProjectNode ).Name == step )
							select tn;
				if ( items.Count() != 0 ) {
					currentNode = items.First();
					if ( tvAdvTfs.SelectedNode != null ) {
						tvAdvTfs.SelectedNode.IsSelected = false;
					}
					currentNode.IsSelected = true;
					currentNode.Expand();
					if ( currentNode.Tag is TfItemNode ) {
						loadSubTree(currentNode.Tag as TfItemNode);
					} else if ( currentNode.Tag is TfTeamProjectNode ) {
						loadSubTree(currentNode.Tag as TfTeamProjectNode);
					}
				} else if ( step == steps[steps.Length - 1] ) {
					var rows = from DataGridViewRow row in dgvFiles.Rows
							   where ( row.DataBoundItem as TfItemNode ).Name == step
							   select row;
					if ( rows.Count() != 0 ) {
						rows.First().Selected = true;
						dgvFiles.Update();
					}
				} else {
					break;
				}

			}
			//currentNode.IsSelected = true;
			_fileNameToSelect = null;
		}

		private void loadSubTree(TfTeamProjectNode projNode)
		{
			if ( projNode == null ) return;
			projNode.Nodes.Clear();
			ItemSet items = projNode.Project.VersionControlServer.GetItems(string.Format("$/{0}/*", projNode.Name));
			foreach ( Item item in items.Items ) {
				if ( item.ItemType == ItemType.File ) continue;
				TfItemNode itemNode = new TfItemNode(item);
				projNode.Nodes.Add(itemNode);
			}
		}

		private void loadSubTree(TfItemNode itemNode)
		{
			if ( itemNode == null ) return;
			if ( itemNode.Type != ItemType.Folder ) return;
			itemNode.Nodes.Clear();
			ItemSet items = itemNode.Item.VersionControlServer.GetItems(string.Format("{0}/*", itemNode.Item.ServerItem));
			foreach ( Item item in items.Items ) {
				if ( item.ItemType == ItemType.File ) continue;
				TfItemNode child = new TfItemNode(item);
				itemNode.Nodes.Add(child);
			}
		}

		private void loadTree()
		{
			_model = new TreeModel();
			_rootNode = new TfServerNode(_tfServer);
			foreach ( TeamProject project in _teamProjects ) {
				TfTeamProjectNode projNode = new TfTeamProjectNode(project);
				loadSubTree(projNode);
				_rootNode.Nodes.Add(projNode);
			}
			_model.Nodes.Add(_rootNode);
			tvAdvTfs.Model = _model;
			tvAdvTfs.CollapseAll();
			loadPreSelectedItem();
		}

		private void lookupServer()
		{
			if ( string.IsNullOrEmpty(txtServerAddress.Text) ) {
				MessageBox.Show("Server address must not be empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
			try {
				_tfServer = new TeamFoundationServer(txtServerAddress.Text);
				_tfServer.Authenticate();
				_versionControl = _tfServer.GetService<VersionControlServer>();
				if ( _versionControl != null ) {
					TeamProject[] projects = _versionControl.GetAllTeamProjects(true);
					_teamProjects = new BindingListEx<TeamProject>();
					_teamProjects.AddRange(projects);
					Workspace[] workspaces = _versionControl.QueryWorkspaces(null, _tfServer.AuthenticatedUserName, null);
					_workSpaces = new BindingListEx<Workspace>();
					foreach ( Workspace workspace in workspaces ) {
						if ( workspace.IsDeleted ) continue;
						_workSpaces.Add(workspace);
					}
					cmbWorkspaces.DataSource = _workSpaces;
					foreach ( var ws in _workSpaces ) {
						if ( ( _workspaceToSelect == null && ws.Computer == Environment.MachineName )
								||
							 ( _workspaceToSelect != null && ws.Name == _workspaceToSelect.Name ) ) {
							cmbWorkspaces.SelectedItem = ws;
							break;
						}
					}
					loadTree();
				}
			} catch ( TeamFoundationServerException tfsEx ) {
				MessageBox.Show(tfsEx.Message, "Team Foundation Server", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
		}

		private void selectItem(string path)
		{
			if ( string.IsNullOrEmpty(path) ) return;
			string[] steps = path.Split('/');
			Node currentNode = _rootNode;
			foreach ( string step in steps ) {
				if ( step == "$" ) continue; // root/server
				var items = from cn in currentNode.Nodes
							where ( cn is TfItemNode && ( cn as TfItemNode ).Name == step ) ||
								  ( cn is TfTeamProjectNode && ( cn as TfTeamProjectNode ).Name == step )
							select cn;
				if ( items.Count() != 0 ) {
					currentNode = items.First();
				}
			}
			//currentNode.IsChecked = true;
			if ( tvAdvTfs.SelectedNode != null ) {
				tvAdvTfs.SelectedNode.Expand();
				tvAdvTfs.SelectedNode.IsSelected = false;
			}
			TreeNodeAdv tna = tvAdvTfs.FindNodeByTag(currentNode);
			tna.Expand(true);
			tna.IsSelected = true;
		}

		#endregion Methods

		#endregion Private Members

		#region Nested Types

		private class ExtensionFilter
		{
			#region Initialization

			public ExtensionFilter(string display, string filter)
			{
				DisplayName = display;
				Filter = filter;
			}

			#endregion Initialization

			#region Public Members

			#region Properties

			public string DisplayName
			{
				get;
				set;
			}

			public string Filter
			{
				get;
				set;
			}

			#endregion Properties

			#endregion Public Members
		}

		#endregion Nested Types
	}

	public interface ITfsExplorer
	{
		#region Properties

		string FileName
		{
			get;
			set;
		}

		string TfsUri
		{
			get;
			set;
		}

		string WorkspaceName
		{
			get;
			set;
		}

		#endregion Properties

		#region Methods

		bool Init();

		#endregion Methods
	}

	[DebuggerDisplay("Name = {Name}, Type = {Type}")]
	public class TfItemNode : Node
	{
		#region Initialization

		public TfItemNode(Item item)
		{
			this.Item = item;
		}

		#endregion Initialization

		#region Public Members

		#region Properties

		public Image Icon
		{
			get
			{
				if ( _folder == null ) {
					_folder = Resources.folder_closed;
					_folder.MakeTransparent();
				}
				if ( _file == null ) {
				}
				return Type == ItemType.Folder ? _folder : _file;
			}
		}

		public Item Item
		{
			get { return this.Tag as Item; }
			set { this.Tag = value; }
		}

		public DateTime LastCheckIn
		{
			get
			{
				//return Item.CheckinDate;
				return (DateTime)this.Item.GetType().GetProperty("CheckinDate")
													.GetValue(this.Item, null);
			}
		}

		public string LocalPath
		{
			get
			{
				Workspace[] workspaces = Item.VersionControlServer.QueryWorkspaces(System.Net.Dns.GetHostName(), null, null);
				if ( workspaces.Length == 0 ) return null;
				Workspace workspace = workspaces[0];
				if ( workspace.IsServerPathMapped(Item.ServerItem) ) {
					string local = workspaces[0].GetLocalItemForServerItem(Item.ServerItem);
					return local;
				} else {
					return "not mapped";
				}
			}
		}

		public string Name
		{
			get
			{
				int index = Item.ServerItem.LastIndexOf('/');
				if ( index < 0 ) return Item.ServerItem;
				return Item.ServerItem.Substring(index + 1);
			}
		}

		public ItemType Type
		{
			get { return Item.ItemType; }
		}

		#endregion Properties

		#endregion Public Members

		#region Private Members

		#region Static Fields

		private static Bitmap _file = null;
		private static Bitmap _folder = null;

		#endregion Static Fields

		#endregion Private Members
	}

	[DebuggerDisplay("Name = {Name}")]
	public class TfServerNode : Node
	{
		#region Initialization

		public TfServerNode(TeamFoundationServer server)
		{
			this.Server = server;
		}

		#endregion Initialization

		#region Public Members

		#region Properties

		public Image Icon
		{
			get
			{
				return Resources.png_server;
			}
		}

		public string Name
		{
			get
			{
				//return Server.Name;
				return this.Server.GetType().GetProperty("Name").GetValue(this.Server, null) as string;
			}
		}

		public TeamFoundationServer Server
		{
			get { return this.Tag as TeamFoundationServer; }
			set { this.Tag = value; }
		}

		public Uri TfsUri
		{
			get { return null; }
		}

		public VersionControlServer VersionControl
		{
			get { return this.Server.GetService<VersionControlServer>(); }
		}

		#endregion Properties

		#endregion Public Members
	}

	[DebuggerDisplay("Name = {Name}")]
	public class TfTeamProjectNode : Node
	{
		#region Initialization

		public TfTeamProjectNode(TeamProject project)
		{
			this.Project = project;
		}

		#endregion Initialization

		#region Public Members

		#region Properties

		public Image Icon
		{
			get
			{
				return Resources.png_folder_green;
			}
		}

		public string Name
		{
			get
			{
				return this.Project.Name;
			}
		}

		public TeamProject Project
		{
			get { return this.Tag as TeamProject; }
			set { this.Tag = value; }
		}

		#endregion Properties

		#endregion Public Members
	}

	public static class UCTfsExplorerFactory
	{
		#region Public Members

		#region Static Methods

		public static ITfsExplorer Create(Control parentContainer, OpenFileDialogEx form)
		{
			UCTfsExplorer tfs = new UCTfsExplorer(form);
			tfs.Dock = DockStyle.Fill;
			parentContainer.Controls.Add(tfs);
			return tfs;
		}

		#endregion Static Methods

		#endregion Public Members
	}
}